#ifndef DESCENT_DETAIL_COMPONENT_HPP_INCLUDED
#define DESCENT_DETAIL_COMPONENT_HPP_INCLUDED

/**
 * @file 
 */

#include <algorithm>
#include <cstdint>
#include <map>
#include <stdexcept>
#include <string>
#include <vector>
#include <boost/any.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include "descent/action.hpp"
#include "descent/attribute.hpp"
#include "descent/log.hpp"
#include "descent/math.hpp"
#include "descent/pointers.hpp"

namespace fs = boost::filesystem;
namespace py = boost::python;

namespace descent {
typedef std::map<std::string, boost::any> attrib_con_t;
typedef std::map<std::string, script> action_con_t;
/**
 * Component.
 * Contains data, logic, and signals, which are called attributes, actions, and signals, respectively.
 * Components are added to entities, which assimilate their constituents.
 * Provide basic units of functionality.
 */
class component : public boost::enable_shared_from_this<component> {
public:
	/**
	 * @param e Owning entity.
	 */
	explicit component(entity_ptr_t e);
	~component();
	/**
	 * Imports a component from an XML file.
	 * @param file Path to the XML file.
	 */
	void import_xml(const fs::path& file);
	/**
	 * Clones the component within its owning entity.
	 */
	void clone();
	/**
	 * Clones the component into an entity.
	 * @param e Entity that owns the cloned component.
	 */
	void clone(entity_ptr_t e);
	/**
	 * Extracts an attribute from the component.
	 * @param T Native type of the attribute.
	 * @param name Name of the attribute.
	 * @return Pointer to the attribute (as its native type). Null is returned if the attribute can't be found or there is a type mismatch.
	 */
	template<typename T>
	T* extract(const typename attrib_con_t::first_type& name) {
		attrib_con_t::iterator i = attribs_.find(name);
		if (attribs_.end() != i) {
			try {
				return &boost::any_cast<T>(i->second);
			}
			catch (const boost::bad_any_cast&) {
				log().warning() << "failed to extract attribute \"" << name << "\" from instance #" << id_ << " of \"" << name_ << "\" component: wrong type\n";
				return NULL;
			}
		}
		log().warning() << "failed to extract attribute \"" << name << "\" from instance #" << id_ << " of \"" << name_ << "\" component: not found\n";
		return NULL;
	}
	/**
	 * Extracts an attribute from the component.
	 * @param T Native type of the attribute.
	 * @param name Name of the attribute.
	 * @return Pointer to the attribute (as its native type). Null is returned if the attribute can't be found or there is a type mismatch.
	 */
	template<typename T>
	const T* extract(const typename attrib_con_t::first_type& name) const {
		return const_cast<component*>(this)->attribute<T>(name);
	}
	/**
	 * Executes an action.
	 * @param name 
	 * @return 
	 */
	py::object execute(const typename action_con_t::first_type& name);
	/**
	 * @return Name (class) of the component.
	 */
	const std::string& name() const;
	/**
	 * @return 
	 */
	unsigned long id() const;
	/**
	 * @return 
	 */
	entity_ptr_t owner();
	/**
	 * @return 
	 */
	const_entity_ptr_t owner() const;
private:
	component(const component& other);
	component& operator=(const component& rhs);
	/// ID seed/source.
	static unsigned long next_id_;
	/// Name (class).
	std::string name_;
	/// ID.
	unsigned long id_;
	/// Owning entity.
	const entity_ptr_t owner_;
	/// Container of attributes.
	attrib_con_t attribs_;
	/// Container of actions.
	action_con_t actions_;
};
/**
 * Extracts an attribute from a component.
 * @param T Attribute type enumerator. The native type is automatically deduced.
 * @param c Component.
 * @param name Name of the attribute.
 * @return Pointer to the attribute (as its native type). Null is returned if the attribute cannot be found.
 */
template<attrib_data_type T>
typename attrib_traits<T>::native_type* extract(component_ptr_t c, const typename attrib_con_t::first_type& name) {
	return c->extract<typename attrib_traits<T>::native_type>(name);
}
/**
 * Extracts an attribute from a component.
 * @param T Attribute type enumerator. The native type is automatically deduced.
 * @param c Component.
 * @param name Name of the attribute.
 * @return Pointer to the attribute (as its native type). Null is returned if the attribute cannot be found.
 */
template<attrib_data_type T>
const typename attrib_traits<T>::native_type* extract(const_component_ptr_t c, const typename attrib_con_t::first_type& name) {
	return c->extract<typename attrib_traits<T>::native_type>(name);
}
}

#endif

